home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue24 / ntserv / SvcClass.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-07-04  |  22.9 KB  |  637 lines

  1. unit SvcClass;
  2.  
  3. { The TNTService class in this unit encapsulates an NT service thread }
  4.  
  5. interface
  6.  
  7. uses Classes, Windows, SysUtils, Registry, WinSvcX, MakeMiC, Logging;
  8.  
  9. type
  10.   PCharArray = ^TCharArray;
  11.   TCharArray = array[0..0] of PChar;
  12.  
  13. type
  14.   TNTService = class;
  15.   TNTServiceClass = class of TNTService;
  16.  
  17.   TNTServiceController = class
  18.   private
  19.     FAvailableServices: TList;
  20.     FServiceMainInstance: Pointer;
  21.     function  ProcessOption: DWORD;
  22.     procedure ServiceMain(NumArgs: DWord; Args: PCharArray); StdCall;
  23.     procedure StartService(Name: Shortstring; Parms: TStrings);
  24.   public
  25.     constructor Create; virtual;
  26.     destructor Destroy; override;
  27.     procedure Connect;
  28.     procedure InstallServices(Names: TStrings);
  29.     procedure RegisterService(SvcClass: TNTServiceClass);
  30.     procedure UnInstallServices(Names: TStrings);
  31.   end;
  32.  
  33.   TNTService = class(TThread)
  34.   private
  35.     FController: TNTServiceController;
  36.     FHandlerInstance: Pointer;
  37.     FNotificationThread: TThread;
  38.     FServiceStatus: TServiceStatus;
  39.     FServicStatusHandle: SERVICE_STATUS_HANDLE;
  40.     procedure DoTerminate; override;
  41.     function GetPaused: Boolean;
  42.     procedure SetCurrentState(Value: DWORD);
  43.     procedure StartNotificationThread;
  44.     procedure TerminateNotificationThread;
  45.   protected
  46.     function AcceptPause: Boolean; virtual;
  47.     function AcceptStop: Boolean; virtual;
  48.     function CanInteract: Boolean; virtual;
  49.     procedure DoHandlerNotification; virtual;
  50.     procedure DoServiceStartup; virtual;
  51.     procedure DoServiceProcessing; virtual; abstract;
  52.     procedure DoServiceCloseDown; virtual;
  53.     procedure Execute; override;
  54.     procedure Handler(Code: Integer); stdcall;
  55.     procedure LogEvent(Severity: DWord; Id: DWord; Inserts: PCharArray; NumInserts: Integer);
  56.     function NeedExtnededElapseTime(Option: DWORD): Boolean; virtual;
  57.     procedure ProcessParms(Parms: TStrings); virtual;
  58.     function WantShutdownNotification: Boolean; virtual;
  59.     property CurrentState: DWORD read FServiceStatus.dwCurrentState write SetCurrentState;
  60.     property Paused: Boolean read GetPaused;
  61.   public
  62.     constructor Create(Parms: TStrings; Controller: TNTServiceController); virtual;
  63.     destructor Destroy; override;
  64.     class procedure DependentServices(List: TStrings); virtual;
  65.     class function ServiceDisplayName: Shortstring; virtual; abstract;
  66.     class function ServiceName: Shortstring; virtual; abstract;
  67.     class function ServiceStartType: DWORD; virtual;
  68.     property Controller: TNTServiceController read FController;
  69.   end;
  70.  
  71. implementation
  72.  
  73. const
  74.   EventRegKey = 'SYSTEM\CurrentControlSet\Services\EventLog\Application\';
  75.  
  76. type
  77.   TNTSCMNotifiyThread = class(TThread)
  78.   private
  79.     FNTService: TNTService;
  80.   protected
  81.     procedure Execute; override;
  82.   public
  83.     constructor Create(Service: TNTService);
  84.   end;
  85.  
  86. constructor TNTSCMNotifiyThread.Create(Service: TNTService);
  87. begin
  88.   inherited Create(False);
  89.   FNTService := Service;
  90. end;
  91.  
  92. procedure TNTSCMNotifiyThread.Execute;
  93. var
  94.   OrigStatus: DWORD;
  95.  
  96. begin
  97.   With FNTService.FServiceStatus do
  98.     begin
  99.       OrigStatus := dwCurrentState;
  100.       dwWaitHint := 5000;
  101.       dwCheckPoint := 0;
  102.     end;
  103.   While not Terminated do
  104.     begin
  105.       MessageBeep(0);
  106.       Sleep(2000);
  107.       With FNTService, FNTService.FServiceStatus do
  108.         if dwCurrentState = OrigStatus then
  109.           begin
  110.             Inc(FServiceStatus.dwCheckPoint);
  111.             SetServiceStatus(FServicStatusHandle,FServiceStatus);
  112.           end;
  113.     end;
  114. end;
  115.  
  116. {==============================================================================}
  117. { TNTServiceController                                                         }
  118. {==============================================================================}
  119.  
  120. Type
  121.   PServiceStartTable = ^TServiceStartTable;
  122.   TServiceStartTable = Array[0..0] Of TServiceTableEntry;
  123.  
  124. type
  125.   ServNameChar = array[0..255] of char;
  126.   TServNameCharArray = array[0..0] of ServNameChar;
  127.   PServNameCharArray = ^TServNameCharArray;
  128.  
  129. constructor TNTServiceController.Create;
  130. begin
  131.   inherited Create;
  132.   FAvailableServices := TList.Create;
  133.   FServiceMainInstance := MakeMethodInstance(@TNTServiceController.ServiceMain,Self);
  134.   if FServiceMainInstance = nil then
  135.     Raise Exception.Create('Failed to create method instance for service main');
  136. end;
  137. {------------------------------------------------------------------------------}
  138. destructor TNTServiceController.Destroy;
  139. begin
  140.   FreeMethodInstance(FServiceMainInstance);
  141.   FAvailableServices.Free;
  142.   inherited Destroy;
  143. end;
  144. {------------------------------------------------------------------------------}
  145. procedure TNTServiceController.Connect;
  146. var
  147.   I: Integer;
  148.   ServiceStartTable: PServiceStartTable;
  149.   ServNameArray: PServNameCharArray;
  150.  
  151. begin
  152.   if FAvailableServices.Count > 0 then
  153.     begin
  154.       ServiceStartTable := AllocMem((FAvailableServices.Count+1)*SizeOf(TServiceTableEntry));
  155.       ServNameArray := AllocMem(FAvailableServices.Count*SizeOf(ServNameChar));
  156.       try
  157.         for I := 0 to FAvailableServices.Count - 1 do
  158.           begin
  159.             StrPCopy(ServNameArray^[I],TNTServiceClass(FAvailableServices[I]).ServiceName);
  160.             ServiceStartTable[I].lpServiceName:= @ServNameArray^[I];
  161.             ServiceStartTable[I].lpServiceProc:= FServiceMainInstance;
  162.           end;
  163.         if not StartServiceCtrlDispatcher(TServiceTableEntry(ServiceStartTable^)) then
  164.           raise Exception.CreateFmt('StartServiceCtrlDispatcher failed with "%d".',[GetLastError]);
  165.       finally
  166.         FreeMem(ServiceStartTable,(FAvailableServices.Count+1)*SizeOf(TServiceTableEntry));
  167.         FreeMem(ServNameArray,FAvailableServices.Count*SizeOf(ServNameChar));
  168.       end;
  169.     end
  170.   else
  171.     raise Exception.Create('No services have been registered to the service controller');
  172. end;
  173. {------------------------------------------------------------------------------}
  174. procedure TNTServiceController.InstallServices(Names: TStrings);
  175. var
  176.   I: Integer;
  177.  
  178.   procedure DoInstall(Entry: Integer);
  179.   var
  180.     hSCManager: SC_Handle;
  181.     hService: SC_Handle;
  182.     NTService: TNTServiceClass;
  183.     WrkServiceDisplayName: array[0..255] of char;
  184.     WrkServiceName: array[0..255] of char;
  185.     DependencyList: TStrings;
  186.     T: Integer;
  187.     PDepDetails: PChar;
  188.     DepLength: Integer;
  189.     Posn: Integer;
  190.     WStr: string;
  191.  
  192.     procedure AddEventDetailsToRegistry;
  193.     var
  194.       EventKey: String;
  195.  
  196.     begin
  197.       WriteLn('Updating registry for event logging');
  198.       EventKey := Format('%s%s',[EventRegKey,NTService.ServiceName]);
  199.       With TRegistry.Create do
  200.        try
  201.          RootKey := HKEY_LOCAL_MACHINE;
  202.          if OpenKey(EventKey,True) then
  203.            try
  204.              WriteInteger('TypesSupported',EVENTLOG_ERROR_TYPE or EVENTLOG_WARNING_TYPE or EVENTLOG_INFORMATION_TYPE);
  205.              WriteString('EventMessageFile',ParamStr(0));
  206.              WriteLn('Registry has been updated for event logging.');
  207.            except
  208.              DeleteKey(EventKey);
  209.              Raise;
  210.            end
  211.          else
  212.            WriteLn(Format('Failed to open key %s',[EventKey]));
  213.        finally
  214.          Free;
  215.        end;
  216.     end;
  217.  
  218.   begin
  219.     NTService := FAvailableServices[Entry];;
  220.     WriteLn(Format('Installing service %s...',[NTService.ServiceName]));
  221.     DependencyList := TStringlist.Create;
  222.     hSCManager:= OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);
  223.     If hSCManager <> 0 then
  224.       try
  225.         NTService.DependentServices(DependencyList);
  226.         if DependencyList.Count > 0 then
  227.           begin
  228.             WriteLn('This service is dependent upon: -');
  229.             DepLength := 1;
  230.             for T := 0 to DependencyList.Count - 1 do
  231.               begin
  232.                 WriteLn(' ' + DependencyList[T]);
  233.                 Inc(DepLength,Length(DependencyList[T]) + 1);
  234.               end;
  235.             PDepDetails := AllocMem(DepLength);
  236.             Posn := 0;
  237.             for T := 0 to DependencyList.Count - 1 do
  238.               begin
  239.                 WStr := DependencyList[T];
  240.                 strmove(PDepDetails + Posn,PChar(WStr),Length(WStr));
  241.                 Inc(Posn,Length(WStr) + 1);
  242.               end;
  243.           end
  244.         else
  245.           PDepDetails := nil;
  246.         StrPCopy(WrkServiceDisplayName,NTService.ServiceDisplayName);
  247.         StrPCopy(WrkServiceName,NTService.ServiceName);
  248.         hService:= CreateService(hSCManager,WrkServiceName,WrkServiceDisplayName,
  249.                                  SERVICE_ALL_ACCESS,ProcessOption,
  250.                                  NTService.ServiceStartType,SERVICE_ERROR_NORMAL,
  251.                                  PChar(ParamStr(0)),nil,nil,PChar(PDepDetails),nil,nil);
  252.         if Assigned(PDepDetails) then
  253.           FreeMem(PDepDetails,DepLength);
  254.         if hService <> 0 then
  255.           begin
  256.             WriteLn('Service was installed successfully.');
  257.             AddEventDetailsToRegistry;
  258.           end
  259.         else
  260.           WriteLn(Format('Failed to create the service. Error was ''%s''',[SysErrorMessage(GetLastError)]));
  261.       finally
  262.         CloseServiceHandle(hSCManager)
  263.       end
  264.     else
  265.       WriteLn(Format('Failed to open Service Control Manager. Error was ''%s''',[SysErrorMessage(GetLastError)]));
  266.     Dependencylist.Free;
  267.   end;
  268.  
  269. begin
  270.   if Names.Count = 0 then
  271.     for I := 0 to FAvailableServices.Count - 1 do
  272.       DoInstall(I)
  273.   else
  274.     for I := 0 to FAvailableServices.Count - 1 do
  275.       if Names.IndexOf(UpperCase(TNTServiceClass(FAvailableServices[I]).ServiceName)) <> -1 then
  276.         DoInstall(I);
  277. end;
  278. {------------------------------------------------------------------------------}
  279. { Sets the value based on the number of registered services.                   }
  280. { Note: SERVICE_INTERACTIVE_PROCESS is not catered for                         }
  281. function TNTServiceController.ProcessOption: DWORD;
  282. begin
  283.   if FAvailableServices.Count > 1 then
  284.     Result := SERVICE_WIN32_SHARE_PROCESS
  285.   else
  286.     Result := SERVICE_WIN32_OWN_PROCESS;
  287. end;
  288. {------------------------------------------------------------------------------}
  289. { Adds the class reference to the internal list                                }
  290. procedure TNTServiceController.RegisterService(SvcClass: TNTServiceClass);
  291. begin
  292.   if FAvailableServices.IndexOf(SvcClass) = -1 then
  293.     FAvailableServices.Add(TObject(SvcClass));
  294. end;
  295. {------------------------------------------------------------------------------}
  296. { This is the entry point for all services in this process. The dispatcher     }
  297. { created a thread then calls this routine (via the MakeMethodInstance jump    }
  298. { block.) After the StartService method creates the new thread for the service }
  299. { this routine terminates thus destroying the thread created by the dispatcher }
  300.  
  301. { According to the MSDN this is OK and I haven't found any problems yet.       }
  302. { However, if this does become a probem you can change StartService to a       }
  303. { function to return the Thread object. You can then do a Waitfor on that      }
  304. { object at the end of this method (or do it in the StartService method)       }
  305.  
  306. Procedure TNTServiceController.ServiceMain(NumArgs: DWord; Args: PCharArray); StdCall;
  307. var
  308.   StartingService: ShortString;
  309.   Parms: TStrings;
  310.   I: Integer;
  311.  
  312. begin
  313.   StartingService := StrPas(Args^[0]);
  314.   Parms := TStringList.Create;
  315.   try
  316.     for I := 1 to NumArgs - 1 do
  317.       Parms.Add(StrPas(Args^[I]));
  318.     StartService(StartingService,Parms);
  319.   finally
  320.     Parms.Free;
  321.   end;
  322. end;
  323. {------------------------------------------------------------------------------}
  324. { Creates an instance using the class reference passed to RegisterService      }
  325. procedure TNTServiceController.StartService(Name: Shortstring; Parms: TStrings);
  326. var
  327.   Instance: TNTService;
  328.   I: Integer;
  329.  
  330. begin
  331.   Instance := nil;
  332.   for I := 0 to FAvailableServices.Count - 1 do
  333.     if TNTServiceClass(FAvailableServices[I]).ServiceName = Name then
  334.       begin
  335.         Instance := TNTService(TNTServiceClass(FAvailableServices[I]).NewInstance);
  336.         try
  337.           Instance.Create(Parms,Self);
  338.           Break;
  339.         except
  340.           Instance.Free;
  341.           Instance := nil;
  342.           break;
  343.         end;
  344.       end;
  345.   if Instance = nil then
  346.     { log failure to start service }
  347.     ;
  348. end;
  349. {------------------------------------------------------------------------------}
  350. procedure TNTServiceController.UnInstallServices(Names: TStrings);
  351. var
  352.   I: Integer;
  353.  
  354.   procedure DoUnInstall(Entry: Integer);
  355.   var
  356.     hSCManager: SC_Handle;
  357.     hService: SC_Handle;
  358.     NTService: TNTServiceClass;
  359.     WrkServiceName: array[0..255] of char;
  360.  
  361.     procedure RemoveEventDetailsFromRegistry;
  362.     var
  363.       EventKey: String;
  364.  
  365.     begin
  366.       WriteLn('Removing event logging registry details.');
  367.       if NTService.ServiceName <> '' then
  368.         begin
  369.           EventKey := Format('%s%s',[EventRegKey,NTService.ServiceName]);
  370.           With TRegistry.Create do
  371.            try
  372.              RootKey := HKEY_LOCAL_MACHINE;
  373.              DeleteKey(EventKey);
  374.              WriteLn('Registry details for event logging has been removed.');
  375.            finally
  376.              Free;
  377.            end;
  378.         end
  379.       else
  380.         WriteLn('Service name missing! Registry not modified.');
  381.     end;
  382.  
  383.   begin
  384.     NTService := FAvailableServices[Entry];;
  385.     WriteLn(Format('Removing service %s...',[NTService.ServiceName]));
  386.     hSCManager := OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);
  387.     If hSCManager <> 0 then
  388.       try
  389.         StrPCopy(WrkServiceName,NTService.ServiceName);
  390.         hService := OpenService(hSCManager,WrkServiceName,SERVICE_ALL_ACCESS);
  391.         if hService <> 0 then
  392.           try
  393.             if DeleteService(hService) then
  394.               begin
  395.                 WriteLn('Service was uninstalled successfully.');
  396.                 RemoveEventDetailsFromRegistry;
  397.               end
  398.             else
  399.               WriteLn(Format('Failed to delete service. Error was ''%s''',[SysErrorMessage(GetLastError)]));
  400.           finally
  401.             CloseServiceHandle(hService);
  402.           end
  403.         else
  404.           WriteLn(Format('Failed to open service "%s": Error was ''%s''',[NTService.ServiceName,SysErrorMessage(GetLastError)]));
  405.       finally
  406.         CloseServiceHandle(hSCManager)
  407.       end
  408.     else
  409.       WriteLn(Format('Failed to open Service control Manager. Error was ''%s''',[SysErrorMessage(GetLastError)]));
  410.   end;
  411.  
  412. begin
  413.   if Names.Count = 0 then
  414.     for I := 0 to FAvailableServices.Count - 1 do
  415.       DoUnInstall(I)
  416.   else
  417.     for I := 0 to FAvailableServices.Count - 1 do
  418.       if Names.IndexOf(UpperCase(TNTServiceClass(FAvailableServices[I]).ServiceName)) <> -1 then
  419.         DoUnInstall(I);
  420. end;
  421.  
  422.  
  423. {==============================================================================}
  424. { TNTService                                                                   }
  425. {==============================================================================}
  426.  
  427.  
  428. {------------------------------------------------------------------------------}
  429. constructor TNTService.Create(Parms: TStrings; Controller: TNTServiceController);
  430. var
  431.   SvcName: array[0..255] of char;
  432.  
  433. begin
  434.   inherited Create(False);
  435.   FController := Controller;
  436.   FreeOnTerminate := True;
  437.   With FServiceStatus do
  438.     begin
  439.       dwServiceType := FController.ProcessOption;
  440.       if CanInteract then
  441.         dwServiceType := dwServiceType or SERVICE_INTERACTIVE_PROCESS;
  442.     end;
  443.   FHandlerInstance := MakeMethodInstance(@TNTService.Handler,Self);
  444.   if FHandlerInstance = nil then
  445.     Raise Exception.Create('Failed to create method instance for service');
  446.   StrPCopy(SvcName,ServiceName);
  447.   FServicStatusHandle := RegisterServiceCtrlHandler(@SvcName,FHandlerInstance);
  448.   if FServicStatusHandle = 0 then
  449.     Raise Exception.CreateFmt('Failed to register service handler. Code: %d',[GetLastError]);
  450.   ProcessParms(Parms);
  451. end;
  452. {------------------------------------------------------------------------------}
  453. destructor TNTService.Destroy;
  454. begin
  455.   FreeMethodInstance(FHandlerInstance);
  456.   inherited Destroy;
  457. end;
  458. {------------------------------------------------------------------------------}
  459. { Whether the service can be paused at this time                               }
  460. function TNTService.AcceptPause: Boolean;
  461. begin
  462.   Result := False;
  463. end;
  464. {------------------------------------------------------------------------------}
  465. { Whether the service can be stopped at this time                              }
  466. function TNTService.AcceptStop: Boolean;
  467. begin
  468.   Result := True;
  469. end;
  470. {------------------------------------------------------------------------------}
  471. { Can service interact with the desktop - not catered for yet                  }
  472. function TNTService.CanInteract: Boolean;
  473. begin
  474.   Result := False;
  475. end;
  476. {------------------------------------------------------------------------------}
  477. { Returns a list of all services which need to be started before this service  }
  478. class procedure TNTService.DependentServices(List: TStrings);
  479. begin
  480. end;
  481. {------------------------------------------------------------------------------}
  482. { When the ControlHandler is called this is executed. It is intended to be     }
  483. { used for services which wait for objects. If they use MsgWaitFor... this     }
  484. { method can post a thread message to satisfy the wait                         }
  485. procedure TNTService.DoHandlerNotification;
  486. begin
  487. end;
  488. {------------------------------------------------------------------------------}
  489. { Any service initialisation here                                              }
  490. procedure TNTService.DoServiceStartup;
  491. begin
  492. end;
  493. {------------------------------------------------------------------------------}
  494. { Any service closedown here                                                   }
  495. procedure TNTService.DoServiceCloseDown;
  496. begin
  497. end;
  498. {------------------------------------------------------------------------------}
  499. procedure TNTService.DoTerminate;
  500. begin
  501.   inherited DoTerminate;
  502.   DoServiceCloseDown;
  503.   CurrentState := SERVICE_STOPPED;
  504. end;
  505. {------------------------------------------------------------------------------}
  506. procedure TNTService.Execute;
  507. begin
  508.   CurrentState := SERVICE_START_PENDING;
  509.   DoServiceStartup;
  510.   CurrentState := SERVICE_RUNNING;
  511.   DoServiceProcessing;
  512. end;
  513. {------------------------------------------------------------------------------}
  514. function TNTService.GetPaused: Boolean;
  515. begin
  516.   Result := (CurrentState = SERVICE_PAUSED);
  517. end;
  518. {------------------------------------------------------------------------------}
  519. { The handler called by the dispatcher. The CurrentState property write method }
  520. { creates a second thread if the state transition is going to be lengthy       }
  521. procedure TNTService.Handler(Code: Integer); StdCall;
  522. begin
  523.   case Code of
  524.     SERVICE_CONTROL_STOP,SERVICE_CONTROL_SHUTDOWN:
  525.       begin
  526.         CurrentState := SERVICE_STOP_PENDING;
  527.         Terminate;
  528.       end;
  529.     SERVICE_CONTROL_PAUSE:
  530.       begin
  531.         CurrentState := SERVICE_PAUSE_PENDING;
  532.       end;
  533.     SERVICE_CONTROL_CONTINUE:
  534.       begin
  535.         CurrentState := SERVICE_CONTINUE_PENDING;
  536.       end;
  537.     SERVICE_CONTROL_INTERROGATE:
  538.       begin
  539.         CurrentState := FServiceStatus.dwCurrentState;
  540.       end;
  541.   end;
  542.   DoHandlerNotification;
  543. end;
  544. {------------------------------------------------------------------------------}
  545. procedure TNTService.LogEvent(Severity: DWord; Id: DWord; Inserts: PCharArray; NumInserts: Integer);
  546. var
  547.   SvcNameChar: Array[0..255] of char;
  548.   EventSource: THandle;
  549.  
  550. begin
  551.   StrPCopy(SvcNameChar,ServiceName);
  552.   EventSource := RegisterEventSource(nil,@SvcNameChar);
  553.   try
  554.     ReportEvent(EventSource,Severity,0,Id,nil,NumInserts,0,PChar(Inserts^),nil);
  555.   finally
  556.     DeRegisterEventSource(EventSource);
  557.   end;
  558. end;
  559. {------------------------------------------------------------------------------}
  560. { The thread created here is responsible for updatibg the SCM                  }
  561. procedure TNTService.StartNotificationThread;
  562. begin
  563.   if not Assigned(FNotificationThread) then
  564.     FNotificationThread := TNTSCMNotifiyThread.Create(Self);
  565. end;
  566. {------------------------------------------------------------------------------}
  567. { Stops the secondary thread.                                                  }
  568. procedure TNTService.TerminateNotificationThread;
  569. begin
  570.   if Assigned(FNotificationThread) then
  571.     begin
  572.       FNotificationThread.Terminate;
  573.       FNotificationThread.WaitFor;
  574.       FNotificationThread := nil;
  575.     end;
  576. end;
  577. {------------------------------------------------------------------------------}
  578. { When overriden this function should return True if the operation (passed as  }
  579. { the Option parameter) is going to take quite a few seconds.                  }
  580. function TNTService.NeedExtnededElapseTime(Option: DWORD): Boolean;
  581. begin
  582.   Result := False;
  583. end;
  584. {------------------------------------------------------------------------------}
  585. { This is called by the constructor and the Parms contain any parameters types }
  586. { by the user in the services control panel applet.                            }
  587. procedure TNTService.ProcessParms(Parms: TStrings);
  588. begin
  589. end;
  590. {------------------------------------------------------------------------------}
  591. { This can be overridden to alter when the service is started. For services    }
  592. { this can only be demand, auto or disabled.                                   }
  593. class function TNTService.ServiceStartType: DWORD;
  594. begin
  595.   Result := SERVICE_DEMAND_START;
  596. end;
  597. {------------------------------------------------------------------------------}
  598. { This is the write property access routine for the CurrentState property.     }
  599. { The FServiceStatus is updated and SetServiceStatus is called to inform the   }
  600. { SCM. For all the PENDING states a NotificationThread is created if the       }
  601. { NeedExtnededElapseTime returns True. When the STOP/RUNNING or PAUSED states  }
  602. { are set the notification thread is terminated (if applicable).               }
  603. procedure TNTService.SetCurrentState(Value: DWORD);
  604. begin
  605.   With FServiceStatus do
  606.     begin
  607.       dwCurrentState := Value;
  608.       dwControlsAccepted := 0;
  609.       if AcceptStop then
  610.         dwControlsAccepted := SERVICE_ACCEPT_STOP;
  611.       if AcceptPause then
  612.         dwControlsAccepted := dwControlsAccepted or SERVICE_ACCEPT_PAUSE_CONTINUE;
  613.       if WantShutdownNotification then
  614.         dwControlsAccepted := dwControlsAccepted or SERVICE_ACCEPT_SHUTDOWN;
  615.     end;
  616.   SetServiceStatus(FServicStatusHandle,FServiceStatus);
  617.   case Value of
  618.     SERVICE_STOPPED,SERVICE_RUNNING,SERVICE_PAUSED:
  619.       begin
  620.         TerminateNotificationThread;
  621.       end;
  622.     SERVICE_START_PENDING,SERVICE_STOP_PENDING,SERVICE_CONTINUE_PENDING,SERVICE_PAUSE_PENDING:
  623.       begin
  624.         if NeedExtnededElapseTime(Value) then
  625.           StartNotificationThread;
  626.       end;
  627.   end;
  628. end;
  629. {------------------------------------------------------------------------------}
  630. function TNTService.WantShutdownNotification: Boolean;
  631. begin
  632.   Result := False;
  633. end;
  634.  
  635. end.
  636.  
  637.